package com.flyxteam.commons.crypto;

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

import org.apache.commons.codec.binary.Base64;

import com.flyxteam.commons.base.consts.GlobalConsts;

/**
 * 使用DES算法进行加密解密
 * <ul>
 * <li>2017-01-01 | ChrisX | + create</li>
 * </ul>
 * @author ChrisX
 */
public class DESUtil {

    private DESUtil() {}

    private static final String CIPHER_DES = "DES";

    /**
     * @param source
     *            要加密的字符串
     * @param key
     *            密钥字符串, 为null使用该类默认key
     * @return 加密的二进制数组
     */
    public static byte[] encrypt(String source, String key) {
        try {
            byte[] sourceBytes = source.getBytes(GlobalConsts.ENCODING);
            // 获取秘钥
            Key keyc = getKey(key);
            // 指定DES对象
            Cipher cipher = Cipher.getInstance(CIPHER_DES);
            // 通过密钥进行初始化(加密模式)
            cipher.init(Cipher.ENCRYPT_MODE, keyc);
            // 执行操作
            return cipher.doFinal(sourceBytes);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);// 如果未提供加密算法会抛此异常
        } catch (NoSuchPaddingException e) {
            throw new RuntimeException(e);// 当请求特定填充机制但该环境中未提供时，抛出此异常
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);// 不支持字符编码
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);// 用于无效 Key（无效的编码、错误的长度、未初始化等）的异常
        } catch (IllegalBlockSizeException e) {
            throw new RuntimeException(e);// 如果提供给块密码的数据长度不正确（即与密码的块大小不匹配），则抛出此异常
        } catch (BadPaddingException e) {
            throw new RuntimeException(e);// 当输入数据期望特定的填充机制而数据又未正确填充时，抛出此异常
        }
    }

    public static byte[] encrypt(String source) {
        return encrypt(source, null);
    }

    /**
     * @param source
     *            要解密的数据
     * @param key
     *            密钥字符串, 为null使用该类默认key
     * @return 解密后的字符串
     */
    public static String decrypt(byte[] source, String key) {
        try {
            // 获取秘钥
            Key keyc = getKey(key);
            // 指定DES对象
            Cipher cipher = Cipher.getInstance(CIPHER_DES);
            // 通过密钥进行初始化(解密模式)
            cipher.init(Cipher.DECRYPT_MODE, keyc);
            // 执行操作
            byte[] dissect = cipher.doFinal(source);
            return new String(dissect);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);// 如果未提供加密算法会抛此异常
        } catch (NoSuchPaddingException e) {
            throw new RuntimeException(e);// 当请求特定填充机制但该环境中未提供时，抛出此异常
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);// 用于无效 Key（无效的编码、错误的长度、未初始化等）的异常
        } catch (IllegalBlockSizeException e) {
            throw new RuntimeException(e);// 如果提供给块密码的数据长度不正确（即与密码的块大小不匹配），则抛出此异常
        } catch (BadPaddingException e) {
            throw new RuntimeException(e);// 当输入数据期望特定的填充机制而数据又未正确填充时，抛出此异常
        }
    }

    public static String decrypt(byte[] source) {
        return decrypt(source, null);
    }

    /**
     * @param key
     *            key字符串, 为null使用固定字符串
     * @return 秘钥
     */
    private static Key getKey(String key) {
        if (key == null)
            return getKey();
        try {
            // 通过固定Base64编码获取二进制数组
            byte[] bs = Base64.decodeBase64(key.getBytes(Charset.forName(GlobalConsts.ENCODING)));
            // 声明Key的规范(DES)
            KeySpec dks = new DESKeySpec(bs);
            // 声明秘钥工厂
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(CIPHER_DES);
            // 获取秘钥
            return keyFactory.generateSecret(dks);
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);// 用于无效 Key（无效的编码、错误的长度、未初始化等）的异常
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);// 如果未提供加密算法会抛此异常
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException(e);// 无效密钥规范的异常
        }
    }

    private static Key getKey() {
        return getKey("s7Oh75I+1ew=");
    }
}
